// import { Grid } from "./../../node_modules/csstype/index.d";
import * as React from "react"
import {
  Box3,
  Mesh,
  Color,
  Scene,
  Group,
  Clock,
  MOUSE,
  Vector2,
  Vector3,
  Material,
  Object3D,
  Raycaster,
  BoxHelper,
  GridHelper,
  DoubleSide,
  LineSegments,
  PlaneGeometry,
  TextureLoader,
  WebGLRenderer,
  SphereGeometry,
  BufferGeometry,
  HemisphereLight,
  DirectionalLight,
  PCFSoftShadowMap,
  MeshBasicMaterial,
  LineBasicMaterial,
  MeshPhongMaterial,
  PerspectiveCamera,
  MeshMatcapMaterial,
  Float32BufferAttribute,
  OrthographicCamera,
  BoxGeometry,
  MeshLambertMaterial,
  ArrowHelper,
  ObjectLoader,
} from "three";
import { DynamicTexture } from "../components/core/DynamicTexture";
import {
  IEdge,
  IFace,
  ILine,
  IVertex,
  Options,
  ZoomOptions,
  EdgeMetadata,
  SceneOptions,
  SphereOptions,
  GlobalMetadata,
} from "../utils/types";
import { log } from "./log";
import * as commonfunc from "../mixins/viewCommonFun";
import { SelectModeEnum } from "../enums";
import { isNullOrUnDef } from "./is";
import { ColorEnum } from "../enums/colorEnum";
import { TrackballControls } from "three/examples/jsm/controls/TrackballControls";
import { Geometry } from "three/examples/jsm/deprecated/Geometry";
import { WebGLRendererParameters } from "three/src/renderers/WebGLRenderer";
import { Texture } from "three/src/textures/Texture";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

import ResizeObserver from "resize-observer-polyfill";

const hasOwnProperty = Object.prototype.hasOwnProperty;

enum CameraType {
  PerspectiveCamera,
  OrthographicCamera,
}
enum ControlsType {
  OrbitControls,
  TrackballControls,
}

interface AttrOption {
  visible?: boolean;
  color?: string | number;
  opacity?: number;
  lineWidth?: number;
}
function test(){
  var objectLoader = new  ObjectLoader();
  objectLoader.load( "models/json/teapot-claraio.json", function ( obj ) {

    console.log(obj)

 } );
}

export default class ThreeJsView {
   
  scene: Scene = new Scene();
  light: HemisphereLight = new HemisphereLight(0xffffff, 0x444444);
  light2: DirectionalLight = new DirectionalLight(0xbbbbbb);
  // controls: OrbitControls | TrackballControls= new TrackballControls(null,null);
  controls: any;
  renderer: WebGLRenderer = new WebGLRenderer();
  camera: PerspectiveCamera | OrthographicCamera = new PerspectiveCamera(
    1,
    1,
    1,
    1
  );
  canvas!: HTMLCanvasElement  ;
  viewContainer!: HTMLElement  ;
  time: Clock = new Clock();
  isVisible = false;
  viewDirty = false;
  lastTimeRendered = 0.0;
  options: Options = {};

  mainGroup: Group = new Group();
  faceGroup: Group = new Group();
  edgeGroup: Group = new Group();
  pointGroup: Group = new Group();

  // State for the Hover Highlighting
  raycaster = new Raycaster();
  highlightedObj: any;
  highlightedIndex = null;
  chooseModel = SelectModeEnum.SURFACE; // 默认选择面

  // State for the Handles
  handles: { dragging: boolean }[] = [];
  gizmoMode = "translate";
  gizmoSpace = "local";

  matcap: Texture = new Texture();
  textureLoader: TextureLoader = new TextureLoader();

  groundMesh: Mesh = new Mesh();
  grid: GridHelper = new GridHelper();

  mouse: { x: number; y: number } = { x: 0, y: 0 };
  active = true;
  frustumSize = 600;

  constructor(opts: Options & { viewContainer: HTMLElement }) {
    debugger
    const { viewContainer, ...options } = opts;
    this.viewContainer = viewContainer;
    Object.assign(this.options, options);
    this.create();
  }
  create(): void {
    debugger
    // Get the current Width and Height of the container Element
    const containerWidth = this.viewContainer.clientWidth;
    const containerHeight = this.viewContainer.clientHeight;
    this.canvas = document.createElement("canvas");
    commonfunc.setCanvas(this.canvas);
    this.viewContainer.appendChild(this.canvas);
    this.renderer = new WebGLRenderer({
      alpha: true, // 是否可以设置背景色透明
      antialias: true, // 是否开启反锯齿
      canvas: this.canvas, // 渲染器绘制其输出的画布。这对应于下面的domElement属性。如果此处未传入，则会创建一个新的画布元素
      // sortObjects: true,
    } ) ;
    //as WebGLRendererParameters & { sortObjects: boolean }
    // Clear bg so we can set it with css
    this.renderer.setClearColor(ColorEnum.BLACK, 0);
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.setSize(containerWidth, containerHeight);
    const resizeObserver = new ResizeObserver(() => this.onWindowResize());
    resizeObserver.observe(this.viewContainer);
    // Create the js Scene
    this.scene = new Scene();
    this.initCamera(CameraType.PerspectiveCamera);
    // Create two lights to evenly illuminate the model and cast shadows
    this.light = new HemisphereLight(0xffffff, 0x444444);
    this.light.position.set(0, 200, 0);
    this.light2 = new DirectionalLight(0xbbbbbb);
    this.light2.position.set(6, 50, -12);
    this.light2.castShadow = true;
    this.light2.shadow.camera.top = 200;
    this.light2.shadow.camera.bottom = -200;
    this.light2.shadow.camera.left = -200;
    this.light2.shadow.camera.right = 200;
    this.light2.shadow.mapSize.width = 128;
    this.light2.shadow.mapSize.height = 128;
    this.scene.add(this.light);
    this.scene.add(this.light2);
    this.renderer.shadowMap.enabled = true;
    this.renderer.shadowMap.type = PCFSoftShadowMap;

    // this.stats?.end();
    this.initControls(ControlsType.TrackballControls);
    // Keep track of the last time the scene was interacted with
    // This allows for lazy rendering to reduce power consumption
    this.controls.addEventListener("change", () => (this.viewDirty = true));
    this.isVisible = true;
    this.viewDirty = true;
    this.time = new Clock();
    this.time.autoStart = true;
    this.lastTimeRendered = 0.0;
    this.initText();
    this.initEvents();

    this.animate();
    // Initialize the view in-case we're lazy rendering...
    this.renderer.render(this.scene, this.camera);
    this.initAxes();
  }
  initAxes() {
    debugger;
    // Make simple orientation arrows and box - REF: http://jsfiddle.net/b97zd1a3/16/
    // arrowCanvas = document.querySelector('#arrowCanvas') as HTMLCanvasElement;
    const arrowCanvas = document.createElement("canvas");
    arrowCanvas.style.width = "150px";
    arrowCanvas.style.height = "150px";
    arrowCanvas.style.position = "absolute";
    arrowCanvas.style.left = "0";
    arrowCanvas.style.top = "300px";
    this.viewContainer.appendChild(arrowCanvas);
    const arrowRenderer = new WebGLRenderer({
      alpha: true,
      antialias: true,
      canvas: arrowCanvas,
    }); // clear
    arrowRenderer.setClearColor(ColorEnum.BLACK, 0);
    arrowRenderer.setSize(
      arrowCanvas.clientWidth * window.devicePixelRatio,
      arrowCanvas.clientHeight * window.devicePixelRatio,
      false
    );
    const arrowScene = new Scene();
    const arrowCamera = new PerspectiveCamera(
      50,
      arrowCanvas.clientWidth / arrowCanvas.clientHeight,
      1,
      500
    );
    const perspectiveCamera = commonfunc.getPerspectiveCamera();
    if (perspectiveCamera) arrowCamera.up = perspectiveCamera.up; // important!
    const arrowPos = new Vector3(0, 0, 0);
    arrowScene.add(
      new ArrowHelper(new Vector3(1, 0, 0), arrowPos, 50, 0x7f2020, 15, 10)
    );
    arrowScene.add(
      new ArrowHelper(new Vector3(0, 1, 0), arrowPos, 50, 0x207f20, 15, 10)
    );
    arrowScene.add(
      new ArrowHelper(new Vector3(0, 0, 1), arrowPos, 50, 0x20207f, 15, 10)
    );
    arrowScene.add(
      new Mesh(
        new BoxGeometry(40, 40, 40),
        new MeshLambertMaterial({
          color: 0xaaaaaa,
          // flatShading: false
        })
      )
    );
    arrowScene.add(new HemisphereLight(0xc7e8ff, 0xffe3b3, 1.2));

    commonfunc.setArrowCanvas(arrowCanvas);
    commonfunc.setArrowRenderer(arrowRenderer);
    commonfunc.setArrowScene(arrowScene);
    commonfunc.setArrowCamera(arrowCamera);

    const userSetting = {
      fontStyle: "bold",
      fontFamily: "Arial",
      faceColor: "Gainsboro",
      edgeColor: "LightSlateGray",
      fontSizeScale: (res) => res / 2,
      resolution: 150,
      xPlusFaceProperty: {
        text: "X+",
        fontColor: "Tomato",
      },
      xMinusFaceProperty: {
        text: "X-",
        fontColor: "Tomato",
      },
      yPlusFaceProperty: {
        text: "Y+",
        fontColor: "SeaGreen",
      },
      yMinusFaceProperty: {
        text: "Y-",
        fontColor: "SeaGreen",
      },
      zPlusFaceProperty: {
        text: "Z+",
        fontColor: "DeepSkyBlue",
      },
      zMinusFaceProperty: {
        text: "Z-",
        fontColor: "DeepSkyBlue",
      },
    };
    const fontList = [
      {
        text: "X+",
        faceName: "xPlus",
        fontColor: "Tomato",
      },
      {
        text: "X-",
        faceName: "xMinus",
        fontColor: "Tomato",
      },
      {
        text: "Y+",
        faceName: "yPlus",
        fontColor: "DeepSkyBlue",
      },
      {
        text: "Y-",
        faceName: "yMinus",
        fontColor: "DeepSkyBlue",
      },
      {
        text: "Z+",
        faceName: "zPlus",
        fontColor: "SeaGreen",
      },
      {
        text: "Z-",
        faceName: "zMinus",
        fontColor: "SeaGreen",
      },
    ];
    const face_textures: DynamicTexture[] = [];

    function createFaceTextures() {
      for (let i = 0; i < 6; i++) {
        const dynamicTexture = new DynamicTexture(512, 512);
        dynamicTexture
          .clear("LightSlateGray")
          .updateFaceTexture(fontList[i].faceName, userSetting);
        face_textures.push(dynamicTexture);
      }
    }

    createFaceTextures();
    const materials = [
      new MeshBasicMaterial({ map: face_textures[0].texture }),
      new MeshBasicMaterial({ map: face_textures[1].texture }),
      new MeshBasicMaterial({ map: face_textures[2].texture }),
      new MeshBasicMaterial({ map: face_textures[3].texture }),
      new MeshBasicMaterial({ map: face_textures[4].texture }),
      new MeshBasicMaterial({ map: face_textures[5].texture }),
    ];
    arrowScene.add(new Mesh(new BoxGeometry(40, 40, 40), materials));

    const orthographicCamera = commonfunc.getOrthographicCamera();
    const orthographicControls = commonfunc.getOrthographicControls();
    const cameraType = commonfunc.getCameraType();
    const perspectiveControls = commonfunc.getPerspectiveControls();

    perspectiveControls?.update();
    orthographicControls?.update();
    if (arrowCamera) {
      if (cameraType === "Perspective" && perspectiveControls) {
        arrowCamera.position.copy(perspectiveCamera.position);
        arrowCamera.position.sub(perspectiveControls.target);
      }
      if (cameraType === "Orthographic" && orthographicControls) {
        arrowCamera.position.copy(orthographicCamera.position);
        arrowCamera.position.sub(orthographicControls.target);
      }
      arrowCamera.lookAt(arrowScene.position);
      arrowCamera.position.setLength(200);
    }
    // this.stats?.begin();
    if (cameraType === "Perspective" && perspectiveControls) {
      arrowRenderer?.render(arrowScene, perspectiveCamera);
    }
    if (cameraType === "Orthographic" && orthographicControls) {
      arrowRenderer?.render(arrowScene, orthographicCamera);
    }
    arrowRenderer?.render(arrowScene, arrowCamera);
    //  this.animate();
  }
  initCamera(type = CameraType.PerspectiveCamera) {
    const containerWidth = this.viewContainer.clientWidth;
    const containerHeight = this.viewContainer.clientHeight;
    commonfunc.setCameraType(type);
    switch (type) {
      case CameraType.PerspectiveCamera:
        this.camera = new PerspectiveCamera(45, 1, 1, 5000);
        this.camera.position.set(50, 100, 150);
        this.camera.lookAt(0, 45, 0);
        break;
      case CameraType.OrthographicCamera:
        const frustumSize = 600;
        const aspect = containerWidth / containerHeight;
        this.camera = new OrthographicCamera(
          (0.5 * frustumSize * aspect) / -2,
          (0.5 * frustumSize * aspect) / 2,
          frustumSize / 2,
          frustumSize / -2,
          150,
          1000
        );
    }
    const canvas = commonfunc.getCanvas();
    const viewSize = 409;
    const far = 100000;
    const aspectRatio = canvas.clientWidth / canvas.clientHeight;
    const orthographicCamera = new OrthographicCamera(
      (-aspectRatio * viewSize) / 2,
      (aspectRatio * viewSize) / 2,
      viewSize / 2,
      -viewSize / 2,
      0.1,
      far
    );
    // initCameraPosition(orthographicCamera);
    commonfunc.setOrthographicCamera(orthographicCamera);

    this.camera.position.set(50, 100, 150);
    this.camera.lookAt(0, 45, 0);
    if (!(this.camera instanceof OrthographicCamera)) {
      this.camera.aspect = containerWidth / containerHeight;
    }
    this.camera.updateProjectionMatrix();
  }

  initControls(type = ControlsType.TrackballControls) {
    if (this.controls) this.controls.dispose();
    switch (type) {
      case ControlsType.OrbitControls:
        this.controls = new OrbitControls(
          this.camera,
          this.renderer.domElement
        );
        this.controls.enableRotate = true;
        // 定义平移时如何平移相机的位置
        this.controls.screenSpacePanning = true;
        this.controls.mouseButtons = {
          LEFT: MOUSE.ROTATE,
          MIDDLE: MOUSE.DOLLY,
          RIGHT: MOUSE.PAN,
        };
        break;
      case ControlsType.TrackballControls:
        this.controls = new TrackballControls(
          this.camera,
          this.renderer.domElement
        );
        // 旋转速度
        this.controls.rotateSpeed = 20.0;
        this.controls.noPan = true;
        this.controls.staticMoving = true;
        this.controls.mouseButtons = {
          LEFT: MOUSE.ROTATE,
          MIDDLE: MOUSE.MIDDLE,
          RIGHT: MOUSE.PAN,
        };
        break;
    }
    // 设置中心(聚焦点)
    this.controls.target.set(0, 45, 0);
    this.controls.panSpeed = 2;
    this.controls.zoomSpeed = 1;
    this.controls.update();

    // 返回包围盒的中心点
    const box = new Box3();
    const center = box.getCenter(new Vector3());
    const camCenter = center.clone();
    const perspectiveControls = commonfunc.createControl(
      commonfunc.getPerspectiveCamera(),
      camCenter
    );
    if (perspectiveControls)
      commonfunc.setPerspectiveControls(perspectiveControls);
  }

  /**
   * 缩放窗口大小
   */
  onWindowResize() {
    const containerWidth = this.viewContainer.clientWidth;
    const containerHeight = this.viewContainer.clientHeight;
    const aspect = containerWidth / containerHeight;

    if (this.camera instanceof PerspectiveCamera) {
      this.camera.aspect = aspect;
      this.camera.updateProjectionMatrix();
    }
    if (this.camera instanceof OrthographicCamera) {
      const frustumSize = this.frustumSize;
      this.camera.left = (-frustumSize * aspect) / 2;
      this.camera.right = (frustumSize * aspect) / 2;
      this.camera.top = frustumSize / 2;
      this.camera.bottom = -frustumSize / 2;
      this.camera.updateProjectionMatrix();
    }

    this.renderer.setSize(containerWidth, containerHeight);

    if (!(this.controls instanceof OrbitControls)) {
      this.controls?.handleResize();
    }

    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.renderer.render(this.scene, this.camera);
    this.viewDirty = true;
  }

  /**
   * 缩放相机到选择
   */
  zoomCameraToSelection(
    group: Group | Object3D,
    opts: ZoomOptions = { fitOffset: 2.0 }
  ) {
    const box = new Box3();
    box.expandByObject(group);

    const size = box.getSize(new Vector3());
    log("模型大小 size: ", size);
    const center = box.getCenter(new Vector3());
    log("模型中心点 center: ", center);

    const maxSize = Math.max(size.x, size.y, size.z);
    let fitHeightDistance = 0;
    let fitWidthDistance = 0;
    if (!(this.camera instanceof OrthographicCamera)) {
      fitHeightDistance =
        maxSize / (2 * Math.atan((Math.PI * this.camera.fov) / 360));
    }
    if (!(this.camera instanceof OrthographicCamera)) {
      fitWidthDistance = fitHeightDistance / this.camera.aspect;
    }
    const distance =
      opts.fitOffset * Math.max(fitHeightDistance, fitWidthDistance);

    const direction = this.controls.target
      .clone()
      .sub(this.camera.position)
      .normalize()
      .multiplyScalar(distance);
    this.controls.maxDistance = distance * 10;
    this.controls.target.copy(center);

    this.camera.near = distance / 100;
    this.camera.far = distance * 100;
    this.camera.updateProjectionMatrix();
    this.camera.position.copy(this.controls.target).sub(direction);

    this.controls.update();

    return { size, center };
  }

  /**
   * 创建box辅助框（包围盒子）
   */
  createBoundingBox(group: Group | Object3D) {
    const boxHelper = new BoxHelper(group, ColorEnum.BLUE);
    this.scene.add(boxHelper);
  }

  /**
   * 创建包围盒的中心点
   */
  createCenterPoint(center: Vector3) {
    const sphere = this.drawSphere(center.clone(), { name: "CENTER_POINT" });
    this.pointGroup.add(sphere);
  }
  /**
   * 绘制小球
   */
  drawSphere(point: Vector3, opts: SphereOptions): Mesh {
    opts = Object.assign(
      { name: null, radius: 1, color: ColorEnum.YELLOW },
      opts
    );
    const geometry = new SphereGeometry(opts.radius, 100, 100);
    const material = new MeshBasicMaterial({ color: opts.color });
    const sphere = new Mesh(geometry, material);
    sphere.position.copy(new Vector3(point.x, point.y, point.z));
    sphere.name = opts.name  ;
    return sphere;
  }
  initText() {
    // Load the Shiny Dull Metal MatCap Material
    this.textureLoader = new TextureLoader();
    this.textureLoader.setCrossOrigin("");
    this.matcap = this.textureLoader.load(
      "/resource/textures/dullFrontLitMetal.png",
      () => {
        this.viewDirty = true;
      }
    );
  }

  combineAndRenderShapes([[faceList, edgeList, pointList], _sceneOptions]) {
    log("faceList: ", faceList);
    log("edgeList: ", edgeList);
    log("pointList: ", pointList);
    //@ts-ignore
    window.workerWorking = false; // Untick this flag to allow Evaluations again

    // 如果faceList为空，则不执行任何操作
    if (!faceList) {
      return;
    }
    // sceneOptions = (sceneOptions || {}) as SceneOptions; .net core

    // The old mainGroup is dead!  Long live the mainGroup!
    this.mainGroup.clear();
    this.scene.remove(this.mainGroup);
    // 开启网格
    // this.createGrid(sceneOptions);

    this.mainGroup = new Group();
    this.mainGroup.name = "shape";
    this.mainGroup.rotation.x = -Math.PI / 2;

    // 设置默认方向
    this.faceGroup.rotation.x = -Math.PI / 2;
    this.edgeGroup.rotation.x = -Math.PI / 2;
    this.pointGroup.rotation.x = -Math.PI / 2;

    this.initFace(faceList);
    this.initEdge(edgeList);

    pointList.forEach((data: IVertex, index: number) => {
      console.log("data: ", data);
      const point = data.vertex_coord;
      const sphere = this.drawSphere(
        new Vector3(point[0], point[1], point[2]),
        {
          radius: 1,
          name: "point_" + index,
        }
      );
      this.pointGroup.add(sphere);
    });

    this.scene.add(this.mainGroup);
    this.scene.add(this.faceGroup);
    this.scene.add(this.edgeGroup);
    this.scene.add(this.pointGroup);
    this.viewDirty = true;
    // zoom to extents
    this.zoomCameraToSelection(this.faceGroup);
    log("Generation Complete!");
    console.timeEnd("模型离散加载时间");

    setTimeout(() => {
      // console.log(this.renderer.info);
      // this.setOpacity(this.faceGroup, 10);
    }, 0);
  }

  /**
   * 初始化面数据
   */
  initFace(faceList: IFace[]) {
    // custom handler face
    this.faceGroup.clear();
    this.faceGroup.name = "Model Faces";
    faceList.forEach((face: IFace) => {
      const vertices: number[] = [],
        normals: number[] = [],
        triangles: number[] = [],
        uvs: number[] = [],
        colors: number[] = [];

      // Copy Vertices into js Vector3 List
      vertices.push(...face.vertex_coord);
      normals.push(...face.normal_coord);
      uvs.push(...face.uv_coord);

      // Sort Triangles into a js Face List
      for (let i = 0; i < face.tri_indexes.length; i += 3) {
        triangles.push(
          face.tri_indexes[i + 0],
          face.tri_indexes[i + 1],
          face.tri_indexes[i + 2]
        );
      }

      // Use Vertex Color to label this face's indices for raycast picking
      for (let i = 0; i < face.vertex_coord.length; i += 3) {
        colors.push(face.face_index, 0, 0);
      }

      const matCapMaterial = new MeshMatcapMaterial({
        matcap: this.matcap,
        side: DoubleSide,
        polygonOffset: true, // Push the mesh back for line drawing
        // polygonOffsetFactor: 2.0,
        // polygonOffsetUnits: 1.0,
      });
      const geometry = new BufferGeometry();
      geometry.setIndex(triangles);
      geometry.setAttribute(
        "position",
        new Float32BufferAttribute(vertices, 3)
      );
      geometry.setAttribute("normal", new Float32BufferAttribute(normals, 3));
      geometry.setAttribute("color", new Float32BufferAttribute(colors, 3));
      geometry.setAttribute("uv", new Float32BufferAttribute(uvs, 2));
      geometry.setAttribute("uv2", new Float32BufferAttribute(uvs, 2));
      geometry.computeBoundingSphere();
      geometry.computeBoundingBox();
      const faceMesh = new Mesh(geometry, matCapMaterial);
      faceMesh.castShadow = true;
      faceMesh.name = "ModelFace_" + face.face_index;
      this.faceGroup.add(faceMesh);
    });
  }
  /**
   * 初始化边框数据
   */
  initEdge(edgeList: IEdge[]) {
    // Add Highlightable Edges to Object
    // This wild complexity is what allows all of the lines to be drawn in a single draw call
    // AND highlighted on a per-edge basis by the mouse hover.  On the docket for refactoring.
    const lineVertices: Vector3[] = [];
    const globalEdgeIndices: number[] = [];
    let curGlobalEdgeIndex = 0;
    // let edgeVertices = 0;
    const globalEdgeMetadata: GlobalMetadata = {};
    globalEdgeMetadata[-1] = { start: -1, end: -1 };
    edgeList.forEach((edge: IEdge) => {
      const edgeMetadata: EdgeMetadata = {};
      edgeMetadata.localEdgeIndex = edge.edge_index;
      edgeMetadata.start = globalEdgeIndices.length;
      for (let i = 0; i < edge.vertex_coord.length - 3; i += 3) {
        // 当前坐标点
        lineVertices.push(
          new Vector3(
            edge.vertex_coord[i + 0],
            edge.vertex_coord[i + 1],
            edge.vertex_coord[i + 2]
          )
        );
        // 下一个坐标点
        lineVertices.push(
          new Vector3(
            edge.vertex_coord[i + 0 + 3],
            edge.vertex_coord[i + 1 + 3],
            edge.vertex_coord[i + 2 + 3]
          )
        );
        globalEdgeIndices.push(curGlobalEdgeIndex); // 对应当前坐标点下标
        globalEdgeIndices.push(curGlobalEdgeIndex); // 对应下一个坐标点下标
        // edgeVertices++;
      }
      edgeMetadata.end = globalEdgeIndices.length - 1;
      globalEdgeMetadata[curGlobalEdgeIndex] = edgeMetadata;
      curGlobalEdgeIndex++;
    });

    const lineGeometry = new BufferGeometry().setFromPoints(lineVertices);
    const lineColors: number[] = [];
    for (let i = 0; i < lineVertices.length; i++) {
      lineColors.push(0, 0, 0);
    }
    lineGeometry.setAttribute(
      "color",
      new Float32BufferAttribute(lineColors, 3)
    );
    const lineMaterial = new LineBasicMaterial({
      color: 0xffffff,
      vertexColors: true,
    });
    const line: ILine = new LineSegments(lineGeometry, lineMaterial);
    line.globalEdgeIndices = globalEdgeIndices;
    line.name = "Model Edges";
    line.lineColors = (lineColors  ) ;
    line.globalEdgeMetadata = globalEdgeMetadata;

    line.highlightEdgeAtLineIndex = (lineIndex) => {
      const edgeIndex: number =
        lineIndex >= 0 ? line.globalEdgeIndices![lineIndex] : lineIndex;
      const startIndex: number = line.globalEdgeMetadata![edgeIndex].start!;
      const endIndex: number = line.globalEdgeMetadata![edgeIndex].end!;
      for (let i = 0; i < line.lineColors!.length; i++) {
        // 列索引
        const colIndex = Math.floor(i / 3);
        (line.lineColors as number[])[i] =
          colIndex >= startIndex && colIndex <= endIndex ? 1 : 0;
      }
      line.geometry.setAttribute(
        "color",
        new Float32BufferAttribute(line.lineColors as number[], 3)
      );
      (line.geometry as unknown as Geometry).colorsNeedUpdate = true;
    };

    line.getEdgeMetadataAtLineIndex = (lineIndex) => {
      return line.globalEdgeMetadata![line.globalEdgeIndices![lineIndex]];
    };

    line.clearHighlights = () => {
      return line.highlightEdgeAtLineIndex!(-1);
    };

    if (this.chooseModel === SelectModeEnum.LINE) {
      this.edgeGroup.add(line);
    } else {
      const lineGroup = new Group();
      lineGroup.name = "Model Edges";
      lineGroup.add(line);
      this.edgeGroup.add(lineGroup);
    }
    // End Adding Highlightable Edges
  }

  /**
   * 开启网格
   */
  createGrid(sceneOptions: SceneOptions) {
    // sceneOptions.gridVisible = true;
    // sceneOptions.groundPlaneVisible = true;
    if (this.groundMesh) {
      this.scene.remove(this.groundMesh);
    }
    // sceneOptions.groundPlaneVisible = true;
    // sceneOptions.gridVisible = true;
    if (sceneOptions.groundPlaneVisible) {
      // Create the ground mesh
      this.groundMesh = new Mesh(
        new PlaneGeometry(2000, 2000),
        new MeshPhongMaterial({
          color: 0x080808,
          depthWrite: true,
          dithering: true,
          polygonOffset: true, // Push the mesh back for line drawing
          polygonOffsetFactor: 6.0,
          polygonOffsetUnits: 1.0,
        })
      );
      this.groundMesh.position.y = -0.1;
      this.groundMesh.rotation.x = -Math.PI / 2;
      this.groundMesh.receiveShadow = true;
      this.scene.add(this.groundMesh);
    }

    if (this.grid) {
      this.scene.remove(this.grid);
    }
    if (sceneOptions.gridVisible) {
      // Create the Ground Grid; one line every 100 units
      this.grid = new GridHelper(2000, 20, 0xcccccc, 0xcccccc);
      this.grid.position.y = -0.01;
      (this.grid.material as Material).opacity = 0.3;
      (this.grid.material as Material).transparent = true;
      this.scene.add(this.grid);
    }
  }

  initEvents() {
    /** Set up the the Mouse Move Callback */
    this.mouse = { x: 0, y: 0 };
    this.viewContainer.addEventListener(
      "mousemove",
      (event) => {
        this.mouse.x = (event.offsetX / this.viewContainer.clientWidth) * 2 - 1;
        this.mouse.y =
          -(event.offsetY / this.viewContainer.clientHeight) * 2 + 1;
      },
      false
    );

    this.viewContainer.addEventListener(
      "click",
      (event) => {
        if (!this.mainGroup) return;

        // const point = this.getMousePoint(event);
        // this.environment.drawSphere(point, { name: 'sphere_1' });

        // update face color
        event.preventDefault();
        const raycaster = new Raycaster();
        const mouse = new Vector2();
        mouse.x =
          (event.clientX / this.renderer.domElement.offsetWidth) * 2 - 1;
        mouse.y =
          -(event.clientY / this.renderer.domElement.offsetHeight) * 2 + 1;
        // mouse.x = (event.offsetX / this.viewContainer.clientWidth) * 2 - 1;
        // mouse.y = -(event.offsetY / this.viewContainer.clientHeight) * 2 + 1;
        raycaster.setFromCamera(this.mouse, this.camera);
        const intersects = raycaster.intersectObjects(this.mainGroup.children);
        // console.log('intersects: ', intersects);
        if (intersects.length) {
          // const colorAttribute = intersects[0].object.geometry.getAttribute('color');
          // const face = intersects[0].face;

          // face.materialIndex = 1;

          // const color = new Color(ColorEnum.red);
          //
          // colorAttribute.setXYZ(face.a, color.r, color.g, color.b);
          // colorAttribute.setXYZ(face.b, color.r, color.g, color.b);
          // colorAttribute.setXYZ(face.c, color.r, color.g, color.b);
          //
          // colorAttribute.needsUpdate = true;

          this.renderer.render(this.scene, this.camera);
        }
      },
      false
    );
  }

  getMousePoint(event) {
    if (!this.mainGroup) return;
    const raycaster = new Raycaster();
    const mouse = new Vector2();
    // 通过鼠标点击位置,计算出 raycaster 所需点的位置,以屏幕为中心点,范围 -1 到 1
    mouse.x = (event.offsetX / this.viewContainer.clientWidth) * 2 - 1;
    mouse.y = -(event.offsetY / this.viewContainer.clientHeight) * 2 + 1;
    // 通过鼠标点击的位置(二维坐标)和当前相机的矩阵计算出射线位置
    raycaster.setFromCamera(mouse, this.camera);
    // 获取与射线相交的对象数组，其中的元素按照距离排序，越近的越靠前
    const intersects = raycaster.intersectObjects(this.mainGroup.children);
    if (intersects.length > 0) {
      // 选中第一个射线相交的物体
      const intersected = intersects[0];
      return intersected.point;
    }
    return null;
  }
  animate() {
    // Don't continue this callback if the View has been destroyed.
    if (!this.active) {
      return;
    }

    requestAnimationFrame(() => this.animate());

    this.controls.update();

    // Lightly Highlight the faces of the object and the current face/edge index
    // This wild complexity is largely to handle the fact that all the faces and lines
    // are being drawn in a single drawcall.  This is also on the docket for refactoring.
    const curGroup =
      SelectModeEnum.SURFACE === this.chooseModel
        ? this.faceGroup
        : this.edgeGroup;
    if (curGroup) {
      this.raycaster.setFromCamera(this.mouse, this.camera);
      const objects: Object3D[] = [];
      if (curGroup?.children) {
        objects.push(...curGroup.children);
      }
      const intersects = this.raycaster.intersectObjects(objects);
      if (intersects.length > 0) {
        const isMesh = intersects[0].object.type === "Mesh";
        const isLine = intersects[0].object.type === "LineSegments";
        // @formatter:off
        const newIndex = isLine
          ? (intersects[0].object as ILine).getEdgeMetadataAtLineIndex!(
              intersects[0].index
            ).localEdgeIndex
          : (
              intersects[0].object as LineSegments
            ).geometry.attributes.color?.getX(intersects[0].face!.a);
        // @formatter:on
        if (
          isLine &&
          (this.highlightedObj !== intersects[0].object ||
            this.highlightedIndex !== newIndex)
        ) {
          if (this.highlightedObj) {
            this.highlightedObj.material.color.setHex(
              this.highlightedObj.currentHex
            );
            if (this.highlightedObj && this.highlightedObj.clearHighlights) {
              this.highlightedObj.clearHighlights();
            }
          }
          this.highlightedObj = intersects[0].object;
          this.highlightedObj.currentHex =
            this.highlightedObj.material.color.getHex();
          this.highlightedObj.material.color.setHex(0xffffff);
          this.highlightedIndex = newIndex;
          if (isLine) {
            this.highlightedObj.highlightEdgeAtLineIndex(intersects[0].index);
          }
          this.viewDirty = true;
          const indexHelper =
            (isLine ? "Edge" : "Face") + " Index: " + this.highlightedIndex;
          this.viewContainer.title = indexHelper;
        }
        if (isMesh && this.highlightedObj !== intersects[0].object) {
          if (this.highlightedObj) {
            if (this.highlightedObj.type === "LineSegments") {
              this.highlightedObj.material.color.setHex(
                this.highlightedObj.currentHex
              ); // 0x000000
              if (this.highlightedObj && this.highlightedObj.clearHighlights) {
                this.highlightedObj.clearHighlights();
              }
            } else {
              this.highlightedObj.material.color.setHex(0xffffff);
              if (this.highlightedObj.name === "Model Face") {
                this.highlightedObj.material.color.setHex(0xffffff);
              }
            }
          }
          this.highlightedObj = intersects[0].object;
          this.highlightedObj.currentHex =
            this.highlightedObj.material.color.getHex();
          this.highlightedObj.material.color.setHex(ColorEnum.PINK);
          this.viewDirty = true;
        }
      } else {
        if (this.highlightedObj) {
          this.highlightedObj.material.color.setHex(
            this.highlightedObj.currentHex
          );
          if (this.highlightedObj.clearHighlights) {
            this.highlightedObj.clearHighlights();
          }
          this.viewDirty = true;
        }

        this.highlightedObj = null;
        this.viewContainer.title = "";
      }
    }

    if (this.handles && this.handles.length > 0) {
      for (let i = 0; i < this.handles.length; i++) {
        this.viewDirty = this.handles[i].dragging || this.viewDirty;
      }
    }

    // Only render the js Viewport if the View is Dirty
    // This saves on rendering time/cost now, but may
    // create headaches in the future.
    if (this.viewDirty) {
      this.renderer.render(this.scene, this.camera);
      this.viewDirty = false;
    }
    // let arrowRenderer= commonfunc.getArrowRenderer
    // arrowRenderer?.render(commonfunc.getArrowScene, commonfunc.getArrowCamera);
  }
  setOpacity(obj: Group | Object3D, opacity: number /* 0~100 */) {
    if (obj instanceof Group) {
      obj.children.forEach((child: Object3D) => {
        this.setOpacity(child, opacity);
      });
    }
    this.updateObjAttr(obj, { opacity });
  }

  // 更新物体属性
  updateObjAttr(
    mesh,
    { visible, color, opacity /* 0~100 */, lineWidth }: AttrOption
  ) {
    if (!mesh || mesh.isLackFace) return;

    if (!isNullOrUnDef(visible)) {
      mesh.visible = visible;
    }

    if (mesh.material) {
      if (!isNullOrUnDef(color)) {
        mesh.material.color = new Color(color);
      }

      if (!isNullOrUnDef(opacity)) {
        mesh.material.opacity = Math.min(1, opacity / 100);
        mesh.material.depthWrite = opacity / 100 === 1;
        mesh.material.transparent = opacity / 100 !== 1;
      }

      if (
        !isNullOrUnDef(lineWidth) &&
        hasOwnProperty.call(mesh.material, "lineWidth")
      ) {
        mesh.material.linewidth = lineWidth;
        mesh.meterial.resolution.set(
          this.viewContainer.clientWidth,
          this.viewContainer.clientHeight
        );
      }
      mesh.material.needsUpdate = true;
    }
  }
}
